/**
*
*	Grain.cpp
*
**/

#include <iostream>
#include <sstream>
#include <stdio.h>

#include <stk/ADSR.h>
//#include "RtWvOut.h" //quizá para callback RtAudio.h
#include <stk/RtAudio.h>
#include <stk/SineWave.h>

const unsigned int PARAM_NUMBER = 4;

using namespace stk;

class Grain{
	public:
		Grain();
		~Grain(void);
		void init();
		void keyOn(void);
		void setBaseFrequency(StkFloat frequency);
		void setTremuloFrequency(StkFloat frequency);
		void setVibratoFrequency(StkFloat frequency);
		void load_configuration(char * path_);
		void save_parameters(char * cparam);
		StkFloat get_parameters(int param);
		StkFloat tick();
		void pinta(){printf("hoal\n");};
		
	protected:
		SineWave vibrato_;	//carrier
		SineWave modulating_;
		SineWave tremulo_;
		ADSR enveloping_FM_;
		ADSR enveloping_out_;
		FILE * pFile_;
		StkFloat base_frequency_;
		StkFrames parameters_;
		
	};

Grain::Grain(){
	//init();
}

Grain::~Grain( void ){
	
}
 
void Grain::init(){
	setBaseFrequency(440.0);
	setVibratoFrequency(1.0);
	setTremuloFrequency(10.0);
	enveloping_FM_.setAllTimes(1, 0.003, 1.0, 0.01); //desde STK_BeeThree.cpp
	enveloping_out_.setAllTimes(1, 0.003, 1.0, 0.01);
}



void Grain::save_parameters(char * cparam){
	int position = 1, endol, i=0;
	parameters_.resize(PARAM_NUMBER+1,1);
	std::string aux2, sparam;
	sparam = cparam;
	endol = sparam.find('\n');
//podría ser que cuando no encuentra \n y devolver -1, al hacer el substring de 0 a -1 diera un error raro, aunque no lo refleja a la hora de pasarlo por pantalla
	if(endol > -1)
		sparam = sparam.substr(0,endol);
	else 
		sparam = sparam.substr(0);
//todo apunta a que el error se producen en el while
//el error era que no inicializaba i!!!!
	while(position > 0){
		position = sparam.find(';');
		aux2 = sparam.substr(0,position);
		parameters_[i] = (StkFloat)strtod(aux2.c_str(),NULL);	//strtod toma todos los caracteres que pudieran ser válidos para formar un número con coma flotante
		position = position + 1;
		sparam = sparam.substr(position);
		i++;
	}
}

void Grain::load_configuration(char * path_){
	pFile_ = fopen(path_,"r");
	char buffer[100];
	while(!feof(pFile_)){
		if(fgets(buffer,100,pFile_) != NULL){
			save_parameters(buffer);
		}
	}
	setBaseFrequency(get_parameters(0));
	setVibratoFrequency(get_parameters(1));
	setTremuloFrequency(get_parameters(2));
	enveloping_FM_.setAllTimes(1, 0.003, 1.0, 0.01); //desde STK_BeeThree.cpp
	enveloping_out_.setAllTimes(1, 0.003, 1.0, 0.01);
		
	fclose(pFile_);
}

StkFloat Grain::get_parameters(int param){
	if(param >= 0 && param < PARAM_NUMBER)
		return parameters_[param];
	else return -1.0;
}

void Grain::keyOn(void){
	enveloping_FM_.keyOn();
	enveloping_out_.keyOn();
	}
void Grain::setBaseFrequency(StkFloat frequency){ 
	base_frequency_ = frequency;
	}
void Grain::setVibratoFrequency(StkFloat frequency){
	vibrato_.setFrequency(frequency);
	}
void Grain::setTremuloFrequency(StkFloat frequency){
	tremulo_.setFrequency(frequency);
	}

StkFloat Grain::tick(){
	StkFloat temp;
	StkFloat temp2;
	temp = 1 + vibrato_.tick();
	temp2 = 1 + tremulo_.tick();
	modulating_.setFrequency(base_frequency_*temp);
	temp = modulating_.tick()*enveloping_FM_.tick();
	temp = (temp*temp2)/2.0;
	temp = temp*enveloping_out_.tick();
	return temp;
	}


//REALTIME CALLBACK
/**
 * This tick() function handles sample computation only. It will be 
 * called automatically when the system needs a new buffer of audio 
 * samples.
 **/

int tick(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, 
double streamTime, RtAudioStreamStatus status, void *dataPointer){
		
		Grain *grano = (Grain *)dataPointer;
		register StkFloat *samples = (StkFloat *) outputBuffer;
		for(unsigned int i = 0;i<nBufferFrames;i++)
				*samples ++ = grano->tick();
		return 0;
}

int main(){
	Stk::setSampleRate(44100.0);
	Grain grano;
	RtAudio dac;
	//Figure out how many bytes in an StkFloat and setup the RtAudio 
	//stream
	
	/*
	 *	De momento el archivo de configuración sólo contiene una línea y su formato 
	 * dentro del fichero es frec_fund;vibrato;tremulo;duración(sin caracter fin de linea)
	 * La duración la añadí por si acaso, pero seguramente no haga falta ahora. Por ahora
	 * son 4 parámetros aunque añadir los distintos tiempos del ADSR no creo que sean problema, 
	 * siempre y cuando se redefina la variable PARAM_NUMBER
	 *
	 */
//Este es el path donde tengo el archivo de configuración en mi equipo
	char path[] = "/home/daviciyo/proyectos/pruebas.txt";
	grano.load_configuration(path);
	
	RtAudio::StreamParameters parameters;
	parameters.deviceId = dac.getDefaultOutputDevice();
	parameters.nChannels = 1;
	RtAudioFormat format = (sizeof(StkFloat) == 8) ? RTAUDIO_FLOAT64 : 
	RTAUDIO_FLOAT32;
	unsigned int bufferFrames = RT_BUFFER_SIZE;
	try{
		dac.openStream( &parameters, NULL, format, (unsigned int)Stk::sampleRate(), &bufferFrames, &tick, (void *)&grano);
		
	}catch(RtError &error){ 
		error.printMessage(); 
		goto cleanup;
	}
	
	//load_configuration(adf,&grano);
	grano.keyOn();
	
	try{
		dac.startStream();
	} catch(RtError &error){
		error.printMessage();
		goto cleanup;
	}
	
	//Block waiting here
	
	char keyhit;
	
	std::cout<<"\n Playing.... press <enter> to quit.\n";
	std::cin.get(keyhit);
	//shut down the output stream
	try{
		dac.closeStream();
	}
	catch(RtError &error){
		error.printMessage();
	}
	
	cleanup:
		return 0;

}



//MAIN PARA RTBLOCKING

/*
int main(){
	
	Stk::setSampleRate(44100.0);
	Stk::showWarnings(true);
	
	RtWvOut * dac;
	Grain grano(440.0);
	grano.setTremuloFrequency(5000.0);
	grano.setVibratoFrequency(20.0);
	StkFloat nFrames = 500000;
	
	try{
		dac = new RtWvOut(1);
		}catch(StkError &){exit(1);}
	
	for(int i = 0;i<nFrames;i++){
		try{
			dac->tick(grano.tick());
		}catch(StkError &){goto cleanup;}
	}
	
	cleanup:
		delete dac;
		
	
return 0;
}*/
